package database;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import page.Page;
import util.StringUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Project Name: mongotest
 * File Name: MongoBaseDao
 * Package Name: com.test.mongo.common
 * Date: 2018/4/27  9:00
 * Copyright (c) 2018, tianyul All Rights Reserved.
 */
@Component
public class MongoBaseDao<T> {
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 查询单条数据
     * @param id
     * @param clazz
     * @return
     */
    public T findOneById(String id, Class<T> clazz) {
        System.out.println("==== findOneById ========");
        return mongoTemplate.findOne(new Query(Criteria.where("id").is(id)), clazz);
    }

    /**
     * 通过信息查询单条
     * @param t
     * @param clazz
     * @return
     */
    public T findOneByBean(T t, Class<T> clazz) {
        System.out.println("==== findOneByBean ========");
        Criteria criteria = transforObjToAndCriteria(t);
        return mongoTemplate.findOne(new Query(criteria), clazz);
    }

    /**
     * 分页查询数据
     * @param pageNum
     * @param pageSize
     * @param clazz
     * @param t
     * @return
     */
    public Page<T> getPage(int pageNum, int pageSize, Class<T> clazz, T t) {
        System.out.println("==== getPage ========");
        //将传过来的非空字段转换成查询条件
        Criteria criteria = transforObjToAndCriteria(t);
        long total;
        List<T> list;
        int skip = (pageNum - 1)*pageSize;
        if(null == criteria) {
            total = mongoTemplate.count(new Query(), clazz);
            list =  mongoTemplate.find(new Query().skip(skip).limit(pageSize), clazz);
        } else {
            total = mongoTemplate.count(new Query(criteria), clazz);
            list =  mongoTemplate.find(new Query(criteria).skip(skip).limit(pageSize), clazz);
        }

        return new Page(pageNum, pageSize, total, list);
    }

    /**
     * 新增，如果主键存在，则抛异常 org.springframework.dao.DuplicateKeyException
     * @param t
     */
    public void insert(T t) {
        mongoTemplate.insert(t);
    }

    /**
     * 新增，如果主键存在，则进行修改操作，否者新增
     * @param t
     */
    public void save(T t) {
        mongoTemplate.save(t);
    }

    /**
     * 查询列表，条件查询
     * @param t
     * @param clazz
     * @return
     */
    public List<T> getList(Class<T> clazz, T t) {

        //将传过来的非空字段转换成查询条件
        Criteria criteria = transforObjToAndCriteria(t);

        return mongoTemplate.find(new Query(criteria), clazz);
    }

    /**
     * 修改单条记录
     * @param t
     * @param clazz
     */
    public void update(T t, Class<T> clazz) {
        // 根据传过来的obj 通过方法，转换成Update()进行修改
        Update update = transforObjToUpdate(t);
        String id = getId(t);
        mongoTemplate.updateFirst(new Query(Criteria.where("id").is(id)), update,clazz);
    }


    /**
     * 修改全部符合条件的记录
     * @param t
     * @param clazz
     */
    public void updateAll(T t, Class<T> clazz) {
        //TODO 没想好
        // 根据传过来的obj 通过方法，转换成Update()进行修改
        Update update = transforObjToUpdate(t);
        String id = getId(t);
        mongoTemplate.updateMulti(new Query(Criteria.where("id").is(id)), update, clazz);

    }

    /**
     * 删除单条
     * @param t
     */
    public void deleteById(T t, Class<T> clazz) {
        String id = getId(t);
        mongoTemplate.remove(new Query(Criteria.where("id").is(id)), clazz);
    }

    /**
     * 将object转换成条件查询--and条件
     * @param object
     * @return
     */
    private Criteria transforObjToAndCriteria(Object object) {
        Criteria criteria = new Criteria();
        if(null == object || checkObjIsNull(object)) {
            return null;
        }

        Class stuCla = object.getClass();// 得到类对象
        Field[] fs = stuCla.getDeclaredFields();//得到属性集合

        List<Criteria> criteriaList = new ArrayList<>();
        try {
            for (Field f : fs) {//遍历属性
                f.setAccessible(true); // 设置属性是可以访问的(私有的也可以)
                Object val = f.get(object);// 得到此属性的值

                if(val != null && !StringUtil.isEmpty(val)) {//这个值不为空，则说明查询该条件
                    criteriaList.add(Criteria.where(f.getName()).is(val));
                }
            }
            Criteria[] criterias = new Criteria[criteriaList.size()];
            for (int i = 0; i < criteriaList.size(); i++) {
                criterias[i] = criteriaList.get(i);
            }
            criteria.andOperator(criterias);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return criteria;
    }

    /**
     * 通过id来获取要修改的参数
     * @param object
     * @return
     */
    private Update transforObjToUpdate(Object object) {
        Update update = new Update();
        if(null == object) {
            return update;
        }

        Class stuCla = object.getClass();// 得到类对象
        Field[] fs = stuCla.getDeclaredFields();//得到属性集合

        try {
            for (Field f : fs) {//遍历属性
                f.setAccessible(true); // 设置属性是可以访问的(私有的也可以)
                Object val = f.get(object);// 得到此属性的值
                if(val != null) {//这个值不为空，说明需要修改该值
                    String name = f.getName();
                    if("id".equals(name)) {
                        continue;
                    }
                    update.set(name, val);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return update;
    }

    /**
     * 获得对象的id
     * @param object
     * @return
     */
    private String getId(Object object) {
        Class stuCla = object.getClass();// 得到类对象
        Field[] fs = stuCla.getDeclaredFields();//得到属性集合

        String id = "";

        try {
            for (Field f : fs) {//遍历属性
                f.setAccessible(true); // 设置属性是可以访问的(私有的也可以)
                Object val = f.get(object);// 得到此属性的值
                    String name = f.getName();
                    if("id".equals(name)) {
                        return String.valueOf(val);
                    }
                }
            } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    /**
     * 获得对象的id
     * @param object
     * @return
     */
    private boolean checkObjIsNull(Object object) {
        Class stuCla = object.getClass();// 得到类对象
        Field[] fs = stuCla.getDeclaredFields();//得到属性集合

        try {
            for (Field f : fs) {//遍历属性
                f.setAccessible(true); // 设置属性是可以访问的(私有的也可以)
                Object val = f.get(object);// 得到此属性的值
                if(val != null && !StringUtil.isEmpty(val)) {
                    return false;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 判断是否是JAVA类还是自定义类
     * @param object
     * @return
     */
    private boolean isJavaClass(Object object) {
        Class clz = object.getClass();
        return clz != null && clz.getClassLoader() == null;
    }


}
